﻿#region

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

#endregion

namespace WMAAN.Uzytki.Kryptografia
{
    public class KryptografiaAES : IKryptografia
    {
        private readonly string domyslnyKlucz;
        private RijndaelManaged kryptoSystem;

        public KryptografiaAES()
        {
            kryptoSystem = new RijndaelManaged();
            domyslnyKlucz = @"s6LnEvIPc5jRxG1Wp9Zw";
        }

        #region IKryptografia Members

        public string szyfruj(string czystyTekst)
        {
            return szyfruj(czystyTekst, domyslnyKlucz);
        }

        public string szyfruj(string czystyTekst, string klucz)
        {
            if (klucz == null || klucz.CompareTo("") == 0)
            {
                throw new CryptographicException("Klucz jest niepoprawny: null lub pusty");
            }
            kryptoSystem = new RijndaelManaged();
            byte[] encrypted;
            byte[] plainBytes;

            //Wykonaj 256 bitowy hash z podanego hasla
            byte[] passBytes = Encoding.UTF8.GetBytes(klucz);
            var sha = new SHA256Managed();

            //Ustaw klucz szyfrujacy na podstawie shashowanego hasla
            kryptoSystem.Key = sha.ComputeHash(passBytes);

            //Utworz nowy wektor inicjalizacyjny
            kryptoSystem.GenerateIV();

            //Utworz encryptor dla podanego klucza i wektora inicjalizujacego
            ICryptoTransform thisEncryptor = kryptoSystem.CreateEncryptor(kryptoSystem.Key, kryptoSystem.IV);

            //Utworz strumien w pamieci
            var msEncrypt = new MemoryStream();
            //Zaszyfruj dane znajdujace sie w strumieniu za pomoca utworzonego encryptora
            var csEncrypt = new CryptoStream(msEncrypt, thisEncryptor, CryptoStreamMode.Write);

            //Skonwertuj dane na tablice bajtow
            plainBytes = Encoding.UTF8.GetBytes(czystyTekst);

            //Zapisz wszystkie dane do crypto systemu
            csEncrypt.Write(plainBytes, 0, plainBytes.Length);
            csEncrypt.FlushFinalBlock();

            //Pobierz zaszyfrowana tablice bajtow i dodaj na poczatku wektor inicjalizacyjny
            encrypted = msEncrypt.ToArray();
            var cryptBytes = new byte[encrypted.Length + 16];
            kryptoSystem.IV.CopyTo(cryptBytes, 0);
            encrypted.CopyTo(cryptBytes, 16);
            msEncrypt.Close();
            csEncrypt.Close();
            return Convert.ToBase64String(cryptBytes);
        }

        public string deszyfruj(string zaszyfrowanyTekst)
        {
            return deszyfruj(zaszyfrowanyTekst, domyslnyKlucz);
        }

        public string deszyfruj(string zaszyfrowanyTekst, string klucz)
        {
            if (klucz == null || klucz.CompareTo("") == 0)
            {
                throw new CryptographicException("Klucz jest niepoprawny: null lub pusty");
            }
            kryptoSystem = new RijndaelManaged();

            //Zarezerwuj pamiec na tablice przechowujaca 16 bajtowy wektor inicjalizacyjny
            var IV = new byte[16];

            //Konwertuj string na tablice bajtow.
            byte[] cryptText = Convert.FromBase64String(zaszyfrowanyTekst);

            //Zarezerwuj pamiec na tablice przechowujaca zaszyfrowany tekst o dlugosci -16
            var cryptBytes = new byte[cryptText.Length - 16];

            //Wydobadz wektor inicjalizacyjny z tablicy bajtow
            Array.Copy(cryptText, 0, IV, 0, 16);

            //Wydobadz zaszyfrowana wiadomosc z tablicy bajtow
            Array.Copy(cryptText, 16, cryptBytes, 0, cryptBytes.Length);

            //Utworz 256 bitowy hash z podanego hasla
            byte[] passBytes = Encoding.UTF8.GetBytes(klucz);
            var sha = new SHA256Managed();

            //Utworz klucz dla algorytmu szyfrujacego na podstawie hashu hasla
            kryptoSystem.Key = sha.ComputeHash(passBytes);

            //Utworz decryptor korzystajacy z tego samego klucza i wektora inicjalizacyjnego,
            //co uzywany w szyfrowaniu wiadomosci
            ICryptoTransform decryptor = kryptoSystem.CreateDecryptor(kryptoSystem.Key, IV);

            //Utworz strumien w pamieci
            var msDecrypt = new MemoryStream(cryptBytes);
            //Odszyfruj dane znajdujace sie w strumieniu za pomoca utworzonego decryptora
            var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

            var plainBytes = new byte[cryptBytes.Length];

            // Odszyfruj tablice bajtow
            int decryptedByteCount = csDecrypt.Read(plainBytes,
                                                    0,
                                                    plainBytes.Length);

            // Zamknij both streams.
            msDecrypt.Close();
            csDecrypt.Close();

            // Convert decrypted data into a string. 
            // Let us assume that the original plaintext string was UTF8-encoded.
            string plainText = Encoding.UTF8.GetString(plainBytes,
                                                       0,
                                                       decryptedByteCount);

            // Return decrypted string.   
            return plainText;
        }

        #endregion
    }
}